1 module spirv_cross;
2 
3 import n = spirv_cross.native;
4 static import spv;
5 
6 class ScCompilationError : Exception
7 {
8     this(string msg)
9     {
10         super(msg);
11     }
12 }
13 
14 class ScError : Exception
15 {
16     this(string msg)
17     {
18         super(msg);
19     }
20 }
21 
22 struct SPIRType;
23 
24 struct Resource
25 {
26     uint id;
27     uint typeId;
28     uint baseTypeId;
29     string name;
30 }
31 
32 struct ShaderResources
33 {
34     Resource[] uniformBuffers;
35     Resource[] storageBuffers;
36     Resource[] stageInputs;
37     Resource[] stageOutputs;
38     Resource[] subpassInputs;
39     Resource[] storageImages;
40     Resource[] sampledImages;
41     Resource[] atomicCounters;
42     Resource[] pushConstantBuffers;
43     Resource[] separateImages;
44     Resource[] separateSamplers;
45 }
46 
47 struct EntryPoint
48 {
49     string name;
50     spv.ExecutionModel executionModel;
51 }
52 
53 struct CombinedImageSampler
54 {
55     /// The ID of the sampler2D variable.
56     uint combinedId;
57     /// The ID of the texture2D variable.
58     uint imageId;
59     /// The ID of the sampler variable.
60     uint samplerId;
61 }
62 
63 struct SpecializationConstant
64 {
65     /// The ID of the specialization constant.
66     uint id;
67     /// The constant ID of the constant, used in Vulkan during pipeline creation.
68     uint constant_id;
69 }
70 
71 struct BufferRange
72 {
73     uint index;
74     size_t offset;
75     size_t range;
76 }
77 
78 /// GLSL precision
79 enum GlslPrecision
80 {
81     dontCare,
82     low,
83     medium,
84     high,
85 }
86 
87 /// Options for the GLSL compiler
88 struct ScOptionsGlsl
89 {
90     /// The shading language version. Corresponds to #version $VALUE.
91     uint ver = 450;
92 
93     /// Emit the OpenGL ES shading language instead of desktop OpenGL.
94     bool es = false;
95 
96     /// Debug option to always emit temporary variables for all expressions.
97     bool forceTemporary = false;
98 
99     /// If true, Vulkan GLSL features are used instead of GL-compatible features.
100     /// Mostly useful for debugging SPIR-V files.
101     bool vulkanSemantics = false;
102 
103     /// If true, gl_PerVertex is explicitly redeclared in vertex, geometry and tessellation shaders.
104     /// The members of gl_PerVertex is determined by which built-ins are declared by the shader.
105     /// This option is ignored in ES versions, as redeclaration in ES is not required, and it depends on a different extension
106     /// (EXT_shader_io_blocks) which makes things a bit more fuzzy.
107     bool separateShaderObjects = false;
108 
109     /// Flattens multidimensional arrays, e.g. float foo[a][b][c] into single-dimensional arrays,
110     /// e.g. float foo[a * b * c].
111     /// This function does not change the actual SPIRType of any object.
112     /// Only the generated code, including declarations of interface variables are changed to be single array dimension.
113     bool flattenMultidimensionalArrays = false;
114 
115     /// For older desktop GLSL targets than version 420, the
116     /// GL_ARB_shading_language_420pack extensions is used to be able to support
117     /// layout(binding) on UBOs and samplers.
118     /// If disabled on older targets, binding decorations will be stripped.
119     bool enable420PackExtension = true;
120 
121     /// GLSL: In vertex shaders, rewrite [0, w] depth (Vulkan/D3D style) to [-w, w] depth (GL style).
122     // MSL: In vertex shaders, rewrite [-w, w] depth (GL style) to [0, w] depth.
123     // HLSL: In vertex shaders, rewrite [-w, w] depth (GL style) to [0, w] depth.
124     bool vertFixupClipspace = false;
125 
126     /// Inverts gl_Position.y or equivalent.
127     bool vertFlipY = false;
128 
129     /// If true, the backend will assume that InstanceIndex will need to apply
130     /// a base instance offset. Set to false if you know you will never use base instance
131     /// functionality as it might remove some internal uniforms.
132     bool vertSupportNonzeroBaseInstance = true;
133 
134     /// Add precision mediump float in ES targets when emitting GLES source.
135     GlslPrecision fragDefaultFloatPrecision = GlslPrecision.medium;
136     /// Add precision highp int in ES targets when emitting GLES source.
137     GlslPrecision fragDefaultIntPrecision = GlslPrecision.medium;
138 }
139 
140 private void scEnforce(n.ScResult res, string msg)
141 {
142     final switch (res)
143     {
144     case n.ScResult.success:
145         return;
146     case n.ScResult.compilationError:
147         throw new ScCompilationError(msg);
148     case n.ScResult.error:
149         throw new ScError(msg);
150     case n.ScResult.unhandled:
151         throw new Exception(msg);
152     }
153 }
154 
155 private void scEnforce(const(n.ScCompiler)* cl, n.ScResult res)
156 {
157     final switch (res)
158     {
159     case n.ScResult.success:
160         return;
161     case n.ScResult.compilationError:
162         throw new ScCompilationError(n.sc_compiler_get_error_string(cl));
163     case n.ScResult.error:
164         throw new ScError(n.sc_compiler_get_error_string(cl));
165     case n.ScResult.unhandled:
166         throw new Exception(n.sc_compiler_get_error_string(cl));
167     }
168 }
169 
170 /// Abstract SPIR-V cross compiler
171 /// Analyses and provides introspection into SPIR-V byte code
172 abstract class ScCompiler
173 {
174     private n.ScCompiler* _cl;
175 
176     private this(n.ScCompiler* cl)
177     {
178         _cl = cl;
179     }
180 
181     ~this()
182     {
183         dispose();
184     }
185 
186     /// Dispose native resources held by the compiler.
187     /// It is called during GC collection, but can be also called manually.
188     void dispose()
189     {
190         if (_cl)
191         {
192             n.sc_compiler_delete(_cl);
193             _cl = null;
194         }
195     }
196 
197     /// After parsing, API users can modify the SPIR-V via reflection and call this
198     /// to disassemble the SPIR-V into the desired langauage.
199     /// Sub-classes actually implement this.
200     string compile()
201     {
202         string result;
203         scEnforce(_cl, n.sc_compiler_compile(_cl, result));
204         return result;
205     }
206 
207     /// Gets the identifier (OpName) of an ID. If not defined, an empty string will be returned.
208     string getName(uint id) const
209     {
210         string result;
211         scEnforce(_cl, n.sc_compiler_get_name(_cl, id, result));
212         return result;
213     }
214 
215     /// Applies a decoration to an ID. Effectively injects OpDecorate.
216     void setDecoration(uint id, spv.Decoration decoration, uint argument = 0)
217     {
218         scEnforce(_cl, n.sc_compiler_set_decoration(_cl, id, decoration, argument));
219     }
220 
221     /// ditto
222     void setDecorationString(uint id, spv.Decoration decoration, string argument)
223     {
224         scEnforce(_cl, n.sc_compiler_set_decoration_string(_cl, id, decoration, argument));
225     }
226 
227     /// Overrides the identifier OpName of an ID.
228     /// Identifiers beginning with underscores or identifiers which contain double underscores
229     /// are reserved by the implementation.
230     void setName(uint id, string name)
231     {
232         scEnforce(_cl, n.sc_compiler_set_name(_cl, id, name));
233     }
234 
235     // const Bitset &get_decoration_bitset(uint id) const;
236 
237     /// Returns whether the decoration has been applied to the ID.
238     bool hasDecoration(uint id, spv.Decoration decoration) const
239     {
240         bool result = void;
241         scEnforce(_cl, n.sc_compiler_has_decoration(_cl, id, decoration, result));
242         return result;
243     }
244 
245     /// Gets the value for decorations which take arguments.
246     /// If the decoration is a boolean (i.e. spv.DecorationNonWritable),
247     /// 1 will be returned.
248     /// If decoration doesn't exist or decoration is not recognized,
249     /// 0 will be returned.
250     uint getDecoration(uint id, spv.Decoration decoration) const
251     {
252         uint result = void;
253         scEnforce(_cl, n.sc_compiler_get_decoration(_cl, id, decoration, result));
254         return result;
255     }
256     /// ditto
257     string getDecorationString(uint id, spv.Decoration decoration) const
258     {
259         string result = void;
260         scEnforce(_cl, n.sc_compiler_get_decoration_string(_cl, id, decoration, result));
261         return result;
262     }
263 
264     /// Removes the decoration for an ID.
265     void unsetDecoration(uint id, spv.Decoration decoration)
266     {
267         scEnforce(_cl, n.sc_compiler_unset_decoration(_cl, id, decoration));
268     }
269 
270     /// Gets the SPIR-V type associated with ID.
271     /// Mostly used with Resource::typeId and Resource::baseTypeId to parse the underlying type of a resource.
272     const(SPIRType)* getType(uint id) const
273     {
274         const(SPIRType)* result = void;
275         scEnforce(_cl, n.sc_compiler_get_type(_cl, id, result));
276         return result;
277     }
278 
279     /// Gets the SPIR-V type of a variable.
280     const(SPIRType)* getTypeFromVariable(uint id) const
281     {
282         const(SPIRType)* result = void;
283         scEnforce(_cl, n.sc_compiler_get_type_from_variable(_cl, id, result));
284         return result;
285     }
286 
287     /// Gets the id of SPIR-V type underlying the given typeId, which might be a pointer.
288     uint getNonPointerTypeId(uint typeId) const
289     {
290         uint result = void;
291         scEnforce(_cl, n.sc_compiler_get_non_pointer_type_id(_cl, typeId, result));
292         return result;
293     }
294 
295     /// Gets the SPIR-V type underlying the given typeId, which might be a pointer.
296     const(SPIRType)* getNonPointerType(uint typeId) const
297     {
298         const(SPIRType)* result = void;
299         scEnforce(_cl, n.sc_compiler_get_non_pointer_type(_cl, typeId, result));
300         return result;
301     }
302 
303     // is_sampled_image
304 
305     /// Gets the underlying storage class for an OpVariable.
306     spv.StorageClass getStorageClass(uint id) const
307     {
308         spv.StorageClass result = void;
309         scEnforce(_cl, n.sc_compiler_get_storage_class(_cl, id, result));
310         return result;
311     }
312 
313     /// If get_name() is an empty string, get the fallback name which will be used
314     /// instead in the disassembled source.
315     string getFallbackName(uint id) const
316     {
317         string result = void;
318         scEnforce(_cl, n.sc_compiler_get_fallback_name(_cl, id, result));
319         return result;
320     }
321 
322     /// If get_name() of a Block struct is an empty string, get the fallback name.
323     /// This needs to be per-variable as multiple variables can use the same block type.
324     string getBlockFallbackName(uint id) const
325     {
326         string result = void;
327         scEnforce(_cl, n.sc_compiler_get_block_fallback_name(_cl, id, result));
328         return result;
329     }
330 
331     /// Given an OpTypeStruct in ID, obtain the identifier for member number "index".
332     /// This may be an empty string.
333     string getMemberName(uint id, uint index) const
334     {
335         string result = void;
336         scEnforce(_cl, n.sc_compiler_get_member_name(_cl, id, index, result));
337         return result;
338     }
339 
340     /// Given an OpTypeStruct in ID, obtain the OpMemberDecoration for member number "index".
341     uint getMemberDecoration(uint id, uint index, spv.Decoration decoration) const
342     {
343         uint result = void;
344         scEnforce(_cl, n.sc_compiler_get_member_decoration(_cl, id, index, decoration, result));
345         return result;
346     }
347     /// ditto
348     string getMemberDecorationString(uint id, uint index, spv.Decoration decoration) const
349     {
350         string result = void;
351         scEnforce(_cl, n.sc_compiler_get_member_decoration_string(_cl, id,
352                 index, decoration, result));
353         return result;
354     }
355 
356     /// Sets the member identifier for OpTypeStruct ID, member number "index".
357     void setMemberName(uint id, uint index, string name)
358     {
359         scEnforce(_cl, n.sc_compiler_set_member_name(_cl, id, index, name));
360     }
361 
362     /// Returns the qualified member identifier for OpTypeStruct ID, member number "index",
363     /// or an empty string if no qualified alias exists
364     string getMemberQualifiedName(uint type_id, uint index) const
365     {
366         string result = void;
367         scEnforce(_cl, n.sc_compiler_get_member_qualified_name(_cl, type_id, index, result));
368         return result;
369     }
370 
371     /// Sets the qualified member identifier for OpTypeStruct ID, member number "index".
372     void setMemberQualifiedName(uint type_id, uint index, string name)
373     {
374         scEnforce(_cl, n.sc_compiler_set_member_qualified_name(_cl, type_id, index, name));
375     }
376 
377     // const Bitset &get_member_decoration_bitset(uint id, uint index) const;
378 
379     /// Returns whether the decoration has been applied to a member of a struct.
380     bool hasMemberDecoration(uint id, uint index, spv.Decoration decoration) const
381     {
382         bool result = void;
383         scEnforce(_cl, n.sc_compiler_has_member_decoration(_cl, id, index, decoration, result));
384         return result;
385     }
386 
387     /// Similar to setDecoration, but for struct members.
388     void setMemberDecoration(uint id, uint index, spv.Decoration decoration, uint argument = 0)
389     {
390         scEnforce(_cl, n.sc_compiler_set_member_decoration(_cl, id, index, decoration, argument));
391     }
392 
393     void setMemberDecorationString(uint id, uint index,
394             spv.Decoration decoration, string argument)
395     {
396         scEnforce(_cl, n.sc_compiler_set_member_decoration_string(_cl, id,
397                 index, decoration, argument));
398     }
399 
400     /// Unsets a member decoration, similar to unsetDecoration.
401     void unsetMemberDecoration(uint id, uint index, spv.Decoration decoration)
402     {
403         scEnforce(_cl, n.sc_compiler_unset_member_decoration(_cl, id, index, decoration));
404     }
405 
406     /// Gets the fallback name for a member, similar to getFallbackName.
407     string getFallbackMemberName(uint index) const
408     {
409         import std.array : array;
410         import std.range : repeat;
411 
412         return repeat('_', index).array;
413     }
414 
415     /// Returns a vector of which members of a struct are potentially in use by a
416     /// SPIR-V shader. The granularity of this analysis is per-member of a struct.
417     /// This can be used for Buffer (UBO), BufferBlock/StorageBuffer (SSBO) and PushConstant blocks.
418     /// ID is the Resource::id obtained from get_shader_resources().
419     BufferRange[] getActiveBufferRanges(uint id) const
420     {
421         BufferRange[] result = void;
422         scEnforce(_cl, n.sc_compiler_get_active_buffer_ranges(_cl, id, result));
423         return result;
424     }
425 
426     /// Returns the effective size of a buffer block.
427     size_t getDeclaredStructSize(const(SPIRType)* struct_type) const
428     {
429         size_t result = void;
430         scEnforce(_cl, n.sc_compiler_get_declared_struct_size(_cl, struct_type, result));
431         return result;
432     };
433 
434     /// Returns the effective size of a buffer block, with a given array size
435     /// for a runtime array.
436     /// SSBOs are typically declared as runtime arrays. getDeclaredStructSize() will return 0 for the size.
437     /// This is not very helpful for applications which might need to know the array stride of its last member.
438     /// This can be done through the API, but it is not very intuitive how to accomplish this, so here we provide a helper function
439     /// to query the size of the buffer, assuming that the last member has a certain size.
440     /// If the buffer does not contain a runtime array, arraySize is ignored, and the function will behave as
441     /// getDeclaredStructSize().
442     /// To get the array stride of the last member, something like:
443     /// getDeclaredStructSizeRuntimeArray(type, 1) - getDeclaredStructSizeRuntimeArray(type, 0) will work.
444     size_t getDeclaredStructSizeRuntimeArray(const(SPIRType)* structType, size_t arraySize) const
445     {
446         size_t result = void;
447         scEnforce(_cl, n.sc_compiler_get_declared_struct_size_runtime_array(_cl,
448                 structType, arraySize, result));
449         return result;
450     }
451 
452     /// Returns the effective size of a buffer block struct member.
453     size_t getDeclaredStructMemberSize(const(SPIRType)* structType, uint index) const
454     {
455         size_t result = void;
456         scEnforce(_cl, n.sc_compiler_get_declared_struct_member_size(_cl,
457                 structType, index, result));
458         return result;
459     }
460 
461     /// Returns a set of all global variables which are statically accessed
462     /// by the control flow graph from the current entry point.
463     /// Only variables which change the interface for a shader are returned, that is,
464     /// variables with storage class of Input, Output, Uniform, UniformConstant, PushConstant and AtomicCounter
465     /// storage classes are returned.
466     ///
467     /// To use the returned set as the filter for which variables are used during compilation,
468     /// this set can be moved to setEnabledInterfaceVariables().
469     uint[] getActiveInterfaceVariables() const
470     {
471         uint[] result = void;
472         scEnforce(_cl, n.sc_compiler_get_active_interface_variables(_cl, result));
473         return result;
474     }
475 
476     /// Sets the interface variables which are used during compilation.
477     /// By default, all variables are used.
478     /// Once set, compile() will only consider the set in active_variables.
479     void setEnabledInterfaceVariables(const(uint)[] activeVariables)
480     {
481         scEnforce(_cl, n.sc_compiler_set_enabled_interface_variables(_cl, activeVariables));
482     }
483 
484     /// Query shader resources, use ids with reflection interface to modify or query binding points, etc.
485     ShaderResources getShaderResources() const
486     {
487         ShaderResources result = void;
488         scEnforce(_cl, n.sc_compiler_get_shader_resources(_cl, result));
489         return result;
490     }
491 
492     /// Query shader resources, but only return the variables which are part of active_variables.
493     /// E.g.: get_shader_resources(get_active_variables()) to only return the variables which are statically
494     /// accessed.
495     ShaderResources getShaderResources(const(uint)[] activeVariables) const
496     {
497         ShaderResources result = void;
498         scEnforce(_cl, n.sc_compiler_get_shader_resources_for_vars(_cl, activeVariables, result));
499         return result;
500     }
501 
502     /// Remapped variables are considered built-in variables and a backend will
503     /// not emit a declaration for this variable.
504     /// This is mostly useful for making use of builtins which are dependent on extensions.
505     void setRemappedVariableState(uint id, bool remapEnable)
506     {
507         scEnforce(_cl, n.sc_compiler_set_remapped_variable_state(_cl, id, remapEnable));
508     }
509     /// ditto
510     bool getRemappedVariableState(uint id) const
511     {
512         bool result = void;
513         scEnforce(_cl, n.sc_compiler_get_remapped_variable_state(_cl, id, result));
514         return result;
515     }
516 
517     /// For subpassInput variables which are remapped to plain variables,
518     /// the number of components in the remapped
519     /// variable must be specified as the backing type of subpass inputs are opaque.
520     void setSubpassInputRemappedComponents(uint id, uint components)
521     {
522         scEnforce(_cl, n.sc_compiler_set_subpass_input_remapped_components(_cl, id, components));
523     }
524     /// ditto
525     uint getSubpassInputRemappedComponents(uint id) const
526     {
527         uint result = void;
528         scEnforce(_cl, n.sc_compiler_get_subpass_input_remapped_components(_cl, id, result));
529         return result;
530     }
531 
532     /// New variants of entry point query and reflection.
533     /// Names for entry points in the SPIR-V module may alias if they belong to different execution models.
534     /// To disambiguate, we must pass along with the entry point names the execution model.
535     EntryPoint[] getEntryPointsAndStages() const
536     {
537         EntryPoint[] result = void;
538         scEnforce(_cl, n.sc_compiler_get_entry_points_and_stages(_cl, result));
539         return result;
540     }
541 
542     /// ditto
543     void setEntryPoint(string entry, spv.ExecutionModel executionModel)
544     {
545         scEnforce(_cl, n.sc_compiler_set_entry_point(_cl, entry, executionModel));
546     }
547 
548     /// ditto
549     void renameEntryPoint(string old_name, string new_name, spv.ExecutionModel executionModel)
550     {
551         scEnforce(_cl, n.sc_compiler_rename_entry_point(_cl, old_name, new_name, executionModel));
552     }
553 
554     /// ditto
555     string getCleansedEntryPointName(string name, spv.ExecutionModel executionModel) const
556     {
557         string result = void;
558         scEnforce(_cl, n.sc_compiler_get_cleansed_entry_point_name(_cl, name,
559                 executionModel, result));
560         return result;
561     }
562 
563     // const Bitset &get_execution_mode_bitset() const;
564 
565     void unsetExecutionMode(spv.ExecutionMode mode)
566     {
567         scEnforce(_cl, n.sc_compiler_unset_execution_mode(_cl, mode));
568     }
569 
570     void setExecutionMode(spv.ExecutionMode mode, uint arg0 = 0, uint arg1 = 0, uint arg2 = 0)
571     {
572         scEnforce(_cl, n.sc_compiler_set_execution_mode(_cl, mode, arg0, arg1, arg2));
573     }
574 
575     /// Gets argument for an execution mode (LocalSize, Invocations, OutputVertices).
576     /// For LocalSize, the index argument is used to select the dimension (X = 0, Y = 1, Z = 2).
577     /// For execution modes which do not have arguments, 0 is returned.
578     uint getExecutionModeArgument(spv.ExecutionMode mode, uint index = 0) const
579     {
580         uint result = void;
581         scEnforce(_cl, n.sc_compiler_get_execution_mode_argument(_cl, mode, index, result));
582         return result;
583     }
584     /// ditto
585     spv.ExecutionModel getExecutionModel() const
586     {
587         spv.ExecutionModel result = void;
588         scEnforce(_cl, n.sc_compiler_get_execution_model(_cl, result));
589         return result;
590     }
591 
592     /// In SPIR-V, the compute work group size can be represented by a constant vector, in which case
593     /// the LocalSize execution mode is ignored.
594     ///
595     /// This constant vector can be a constant vector, specialization constant vector, or partly specialized constant vector.
596     /// To modify and query work group dimensions which are specialization constants, SPIRConstant values must be modified
597     /// directly via get_constant() rather than using LocalSize directly. This function will return which constants should be modified.
598     ///
599     /// To modify dimensions which are *not* specialization constants, set_execution_mode should be used directly.
600     /// Arguments to set_execution_mode which are specialization constants are effectively ignored during compilation.
601     /// NOTE: This is somewhat different from how SPIR-V works. In SPIR-V, the constant vector will completely replace LocalSize,
602     /// while in this interface, LocalSize is only ignored for specialization constants.
603     ///
604     /// The specialization constant will be written to x, y and z arguments.
605     /// If the component is not a specialization constant, a zeroed out struct will be written.
606     /// The return value is the constant ID of the builtin WorkGroupSize, but this is not expected to be useful
607     /// for most use cases.
608     uint getWorkGroupSizeSpecializationConstants(out SpecializationConstant x,
609             out SpecializationConstant y, out SpecializationConstant z) const
610     {
611         uint result = void;
612         scEnforce(_cl, n.sc_compiler_get_work_group_size_specialization_constants(_cl,
613                 x, y, z, result));
614         return result;
615     }
616 
617     /// Analyzes all OpImageFetch (texelFetch) opcodes and checks if there are instances where
618     /// said instruction is used without a combined image sampler.
619     /// GLSL targets do not support the use of texelFetch without a sampler.
620     /// To workaround this, we must inject a dummy sampler which can be used to form a sampler2D at the call-site of
621     /// texelFetch as necessary.
622     ///
623     /// This must be called before buildCombinedImageSamplers().
624     /// buildCombinedImageSamplers() may refer to the ID returned by this method if the returned ID is non-zero.
625     /// The return value will be the ID of a sampler object if a dummy sampler is necessary, or 0 if no sampler object
626     /// is required.
627     ///
628     /// If the returned ID is non-zero, it can be decorated with set/bindings as desired before calling compile().
629     /// Calling this function also invalidates getActiveInterfaceVariables(), so this should be called
630     /// before that function.
631     uint buildDummySamplerForCombinedImages()
632     {
633         uint result = void;
634         scEnforce(_cl, n.sc_compiler_build_dummy_sampler_for_combined_images(_cl, result));
635         return result;
636     }
637 
638     /// Analyzes all separate image and samplers used from the currently selected entry point,
639     /// and re-routes them all to a combined image sampler instead.
640     /// This is required to "support" separate image samplers in targets which do not natively support
641     /// this feature, like GLSL/ESSL.
642     ///
643     /// This must be called before compile() if such remapping is desired.
644     /// This call will add new sampled images to the SPIR-V,
645     /// so it will appear in reflection if getShaderResources() is called after buildCombinedImageSamplers.
646     ///
647     /// If any image/sampler remapping was found, no separate image/samplers will appear in the decompiled output,
648     /// but will still appear in reflection.
649     ///
650     /// The resulting samplers will be void of any decorations like name, descriptor sets and binding points,
651     /// so this can be added before compile() if desired.
652     ///
653     /// Combined image samplers originating from this set are always considered active variables.
654     /// Arrays of separate samplers are not supported, but arrays of separate images are supported.
655     /// Array of images + sampler -> Array of combined image samplers.
656     void buildCombinedImageSamplers()
657     {
658         scEnforce(_cl, n.sc_compiler_build_combined_image_samples(_cl));
659     }
660 
661     /// Gets a remapping for the combined image samplers.
662     CombinedImageSampler[] getCombinedImageSamplers() const
663     {
664         CombinedImageSampler[] result = void;
665         scEnforce(_cl, n.sc_compiler_get_combined_image_samplers(_cl, result));
666         return result;
667     }
668 
669     // void set_variable_type_remap_callback(VariableTypeRemapCallback cb)
670 
671     /// API for querying which specialization constants exist.
672     /// To modify a specialization constant before compile(), use get_constant(constant.id),
673     /// then update constants directly in the SPIRConstant data structure.
674     /// For composite types, the subconstants can be iterated over and modified.
675     /// constant_type is the SPIRType for the specialization constant,
676     /// which can be queried to determine which fields in the unions should be poked at.
677     SpecializationConstant[] getSpecializationConstants() const
678     {
679         SpecializationConstant[] result = void;
680         scEnforce(_cl, n.sc_compiler_get_specialization_constants(_cl, result));
681         return result;
682     }
683 
684     // SPIRConstant &get_constant(uint id);
685     // const SPIRConstant &get_constant(uint id) const;
686 
687     uint getCurrentIdBound() const
688     {
689         uint result = void;
690         scEnforce(_cl, n.sc_compiler_get_current_id_bound(_cl, result));
691         return result;
692     }
693 
694     /// API for querying buffer objects.
695     /// The type passed in here should be the base type of a resource, i.e.
696     /// getType(resource.baseTypeId)
697     /// as decorations are set in the basic Block type.
698     /// The type passed in here must have these decorations set, or an exception is raised.
699     /// Only UBOs and SSBOs or sub-structs which are part of these buffer types will have these decorations set.
700     uint typeStructMemberOffset(const(SPIRType)* type, uint index) const
701     {
702         uint result = void;
703         scEnforce(_cl, n.sc_compiler_type_struct_member_offset(_cl, type, index, result));
704         return result;
705     }
706 
707     /// ditto
708     uint typeStructMemberArrayStride(const(SPIRType)* type, uint index) const
709     {
710         uint result = void;
711         scEnforce(_cl, n.sc_compiler_type_struct_member_array_stride(_cl, type, index, result));
712         return result;
713     }
714 
715     /// ditto
716     uint typeStructMemberMatrixStride(const(SPIRType)* type, uint index) const
717     {
718         uint result = void;
719         scEnforce(_cl, n.sc_compiler_type_struct_member_matrix_stride(_cl, type, index, result));
720         return result;
721     }
722 
723     /// Gets the offset in SPIR-V words (uint) for a decoration which was originally declared in the SPIR-V binary.
724     /// The offset will point to one or more uint literals which can be modified in-place before using the SPIR-V binary.
725     /// Note that adding or removing decorations using the reflection API will not change the behavior of this function.
726     /// If the decoration was declared, sets the word_offset to an offset into the provided SPIR-V binary buffer and returns true,
727     /// otherwise, returns false.
728     /// If the decoration does not have any value attached to it (e.g. DecorationRelaxedPrecision), this function will also return false.
729     bool getBinaryOffsetForDecoration(uint id, spv.Decoration decoration, out uint word_offset) const
730     {
731         bool result = void;
732         scEnforce(_cl, n.sc_compiler_get_binary_offset_for_decoration(_cl, id,
733                 decoration, word_offset, result));
734         return result;
735     }
736 
737     /// HLSL counter buffer reflection interface.
738     /// Append/Consume/Increment/Decrement in HLSL is implemented as two "neighbor" buffer objects where
739     /// one buffer implements the storage, and a single buffer containing just a lone "int" implements the counter.
740     /// To SPIR-V these will be exposed as two separate buffers, but glslang HLSL frontend emits a special indentifier
741     /// which lets us link the two buffers together.
742     ///
743     /// Queries if a variable ID is a counter buffer which "belongs" to a regular buffer object.
744     ///
745     /// If SPV_GOOGLE_hlsl_functionality1 is used, this can be used even with a stripped SPIR-V module.
746     /// Otherwise, this query is purely based on OpName identifiers as found in the SPIR-V module, and will
747     /// only return true if OpSource was reported HLSL.
748     /// To rely on this functionality, ensure that the SPIR-V module is not stripped.
749     bool bufferIsHlslCounterBuffer(uint id) const
750     {
751         bool result = void;
752         scEnforce(_cl, n.sc_compiler_buffer_is_hlsl_counter_buffer(_cl, id, result));
753         return result;
754     }
755 
756     /// Queries if a buffer object has a neighbor "counter" buffer.
757     /// If so, the ID of that counter buffer will be returned in counter_id.
758     /// If SPV_GOOGLE_hlsl_functionality1 is used, this can be used even with a stripped SPIR-V module.
759     /// Otherwise, this query is purely based on OpName identifiers as found in the SPIR-V module, and will
760     /// only return true if OpSource was reported HLSL.
761     /// To rely on this functionality, ensure that the SPIR-V module is not stripped.
762     bool bufferGetHlslCounterBuffer(uint id, out uint counterId)
763     {
764         bool result = void;
765         scEnforce(_cl, n.sc_compiler_buffer_get_hlsl_counter_buffer(_cl, id, counterId, result));
766         return result;
767     }
768 
769     /// Gets the list of all SPIR-V Capabilities which were declared in the SPIR-V module.
770     spv.Capability[] getDeclaredCapabilities() const
771     {
772         spv.Capability[] result = void;
773         scEnforce(_cl, n.sc_compiler_get_declared_capabilities(_cl, result));
774         return result;
775     }
776 
777     /// Gets the list of all SPIR-V extensions which were declared in the SPIR-V module.
778     string[] getDeclaredExtensions() const
779     {
780         string[] result = void;
781         scEnforce(_cl, n.sc_compiler_get_declared_extensions(_cl, result));
782         return result;
783     }
784 
785     /// When declaring buffer blocks in GLSL, the name declared in the GLSL source
786     /// might not be the same as the name declared in the SPIR-V module due to naming conflicts.
787     /// In this case, SPIRV-Cross needs to find a fallback-name, and it might only
788     /// be possible to know this name after compiling to GLSL.
789     /// This is particularly important for HLSL input and UAVs which tends to reuse the same block type
790     /// for multiple distinct blocks. For these cases it is not possible to modify the name of the type itself
791     /// because it might be unique. Instead, you can use this interface to check after compilation which
792     /// name was actually used if your input SPIR-V tends to have this problem.
793     /// For other names like remapped names for variables, etc, it's generally enough to query the name of the variables
794     /// after compiling, block names are an exception to this rule.
795     /// ID is the name of a variable as returned by Resource::id, and must be a variable with a Block-like type.
796     ///
797     /// This also applies to HLSL cbuffers.
798     string getRemappedDeclaredBlockName(uint id) const
799     {
800         string result = void;
801         scEnforce(_cl, n.sc_compiler_get_remapped_declared_block_name(_cl, id, result));
802         return result;
803     }
804 
805     //Bitset get_buffer_block_flags(uint id) const;
806 }
807 
808 /// Compiler that produces Glsl code
809 class ScCompilerGlsl : ScCompiler
810 {
811 
812     private @property inout(n.ScCompilerGlsl)* glsl() inout
813     {
814         return cast(inout(n.ScCompilerGlsl)*) _cl;
815     }
816 
817     this(in uint[] ir)
818     {
819         n.ScCompilerGlsl* cl;
820         string msg;
821         const res = n.sc_compiler_glsl_new(ir, n.gcCallbacks, cl, msg);
822         scEnforce(res, msg);
823         super(cast(n.ScCompiler*) cl);
824     }
825 
826     @property ScOptionsGlsl options() const
827     {
828         ScOptionsGlsl opts;
829         n.sc_compiler_glsl_get_options(glsl, opts);
830         return opts;
831     }
832 
833     @property void options(ScOptionsGlsl opts)
834     {
835         n.sc_compiler_glsl_set_options(glsl, &opts);
836     }
837 
838     /// Returns the current string held in the conversion buffer. Useful for
839     /// capturing what has been converted so far when compile() throws an error.
840     string getPartialSource()
841     {
842         string result = void;
843         scEnforce(_cl, n.sc_compiler_glsl_get_partial_source(glsl, result));
844         return result;
845     }
846 
847     /// Adds a line to be added right after #version in GLSL backend.
848     /// This is useful for enabling custom extensions which are outside the scope of SPIRV-Cross.
849     /// This can be combined with variable remapping.
850     /// A new-line will be added.
851     ///
852     /// While addHeaderLine() is a more generic way of adding arbitrary text to the header
853     /// of a GLSL file, requireExtension() should be used when adding extensions since it will
854     /// avoid creating collisions with SPIRV-Cross generated extensions.
855     ///
856     /// Code added via add_header_line() is typically backend-specific.
857     void addHeaderLine(string str)
858     {
859         scEnforce(_cl, n.sc_compiler_glsl_add_header_line(glsl, str));
860     }
861 
862     /// Adds an extension which is required to run this shader, e.g.
863     /// require_extension("GL_KHR_my_extension");
864     void requireExtension(string ext)
865     {
866         scEnforce(_cl, n.sc_compiler_glsl_require_extension(glsl, ext));
867     }
868 
869     /// Legacy GLSL compatibility method.
870     /// Takes a uniform or push constant variable and flattens it into a (i|u)vec4 array[N]; array instead.
871     /// For this to work, all types in the block must be the same basic type, e.g. mixing vec2 and vec4 is fine, but
872     /// mixing int and float is not.
873     /// The name of the uniform array will be the same as the interface block name.
874     void flattenBufferBlock(uint id)
875     {
876         scEnforce(_cl, n.sc_compiler_glsl_flatten_buffer_block(glsl, id));
877     }
878 
879 }